package com.lang8.util;

import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

import org.apache.commons.lang3.ArrayUtils;



public class SignatureUtil
{
	private static String priKey_Hex_Str="30820275020100300d06092a864886f70d01010105000482025f3082025b020100028181009c4a27c405801028b66906ceec629aedc21ef6b36c5055201df500971598ee73d4da5f8f5a576ce7adc4d14ecf38d48f2c6c85a909e2f6306457066ed4734ce43261c02d56ad9c9031ba81dec074f6265647dd155d6eb04e70df7a62949a86de464a2055781bd9968e387ae4397eb6ed55851b0cd9ed1940bae7898dad97305902030100010281802c90f8c5eccb97d00433eebd12ab0b82c0d813811661d1e456a686848ea40d5afa574ae895da5833b859675611c122dbb0cf414df283c5b006ecc9e8d0ac10408ff683aa61c051e104163a005adbed91537c8c8ee11988c14c70257a9f00c63540cdfecdd95157890118b7aca34066dc4424e6c16aa69c90ecb4e6ecce9c0401024100cf63d496e1d94fcd7aa40f1dc0662b9ceb6be4b1aa8cc18441857f3850993e346928743af4c1afcff0b95192d5c4716fe5e9a34b7c9ee619c22d55bc9f019613024100c0ec1dfc84e6e15855041040b6e7394263a7ce2694854dfbde044197cb14f0408fefb47d404b77713c98af61ada195c13afd126893fd7183c6acc8655d5e1d63024025919011451878bc605b17e1ebd8964df6d7b4b5fa8f44ff105f7e8c550d6d9750f56318cafdcb3b80001226e7528eba168c1c68b04a98a451e8a47f66ce82c10240214698c57d86f5476f228c4b1d4134b726c942dd94b0898aeff87bf7daa779e928ea878f330fd1424ed96726c9f41d087f4c3768a25e0cbe55fe677254706f5102402b6cf6d38ec220fcb1aa080b9afe469c77e55b024dfae2ea058065c5eac5e8eab911fb9a788baccfd444b04f97594702c4bf0ffaa0d00fddce027f63b03b2456";
	private static String pubKey_Hex_Str="30819f300d06092a864886f70d010101050003818d00308189028181009c4a27c405801028b66906ceec629aedc21ef6b36c5055201df500971598ee73d4da5f8f5a576ce7adc4d14ecf38d48f2c6c85a909e2f6306457066ed4734ce43261c02d56ad9c9031ba81dec074f6265647dd155d6eb04e70df7a62949a86de464a2055781bd9968e387ae4397eb6ed55851b0cd9ed1940bae7898dad9730590203010001";

	/**
	 * 本方法使用SHA1withRSA签名算法产生签名
	 * 
	 * @param String
	 *            priKey 签名时使用的私钥(16进制编码)
	 * @param String
	 *            src 签名的原字符串
	 * @return String 签名的返回结果(16进制编码)。当产生签名出错的时候，返回null。
	 */
	public static String generateSHA1withRSASigature(String priKey,String src)
	{
		try
		{

			Signature sigEng=Signature.getInstance("SHA1withRSA");

			byte[] pribyte=hexStrToBytes(priKey.trim());

			PKCS8EncodedKeySpec keySpec=new PKCS8EncodedKeySpec(pribyte);
			KeyFactory fac=KeyFactory.getInstance("RSA");

			RSAPrivateKey privateKey=(RSAPrivateKey)fac.generatePrivate(keySpec);
			sigEng.initSign(privateKey);
			sigEng.update(src.getBytes());

			byte[] signature=sigEng.sign();
			return bytesToHexStr(signature);
		}
		catch(Exception e)
		{
			e.toString();
			return null;
		}
	}

	/**
	 * 本方法使用SHA1withRSA签名算法验证签名
	 * 
	 * @param String
	 *            pubKey 验证签名时使用的公钥(16进制编码)
	 * @param String
	 *            sign 签名结果(16进制编码)
	 * @param String
	 *            src 签名的原字符串
	 * @return String 签名的返回结果(16进制编码)
	 */
	public static boolean verifySHA1withRSASigature(String pubKey,String sign,String src)
	{
		try
		{
			Signature sigEng=Signature.getInstance("SHA1withRSA");

			byte[] pubbyte=hexStrToBytes(pubKey.trim());

			X509EncodedKeySpec keySpec=new X509EncodedKeySpec(pubbyte);
			KeyFactory fac=KeyFactory.getInstance("RSA");
			RSAPublicKey rsaPubKey=(RSAPublicKey)fac.generatePublic(keySpec);

			sigEng.initVerify(rsaPubKey);
			sigEng.update(src.getBytes());

			byte[] sign1=hexStrToBytes(sign);
			return sigEng.verify(sign1);

		}
		catch(Exception e)
		{
			e.toString();
			return false;
		}
	}

	/**
	 * 本方法用于产生1024位RSA公私钥对。
	 */
	public static void genRSAKeyPair()
	{
		KeyPairGenerator rsaKeyGen=null;
		KeyPair rsaKeyPair=null;
		try
		{
			System.out.println("Generating a pair of RSA key ... ");
			rsaKeyGen=KeyPairGenerator.getInstance("RSA");
			SecureRandom random=new SecureRandom();
			String tempStr="1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
			int length=tempStr.length();
			String temp="";
			for(int i=0;i<50;i++)
			{
				int randomInt=random.nextInt(length);
				temp+=tempStr.substring(randomInt,randomInt+1);
			}
			random.setSeed(temp.getBytes());

			rsaKeyGen.initialize(1024,random);
			rsaKeyPair=rsaKeyGen.genKeyPair();
			PublicKey rsaPublic=rsaKeyPair.getPublic();
			PrivateKey rsaPrivate=rsaKeyPair.getPrivate();

			System.out.println("公钥："+bytesToHexStr(rsaPublic.getEncoded()));
			System.out.println("私钥："+bytesToHexStr(rsaPrivate.getEncoded()));
			System.out.println("1024-bit RSA key GENERATED.");
		}
		catch(Exception e)
		{
			System.out.println("genRSAKeyPair："+e);
		}
	}

	/**
	 * 将字节数组转换为16进制字符串的形式.
	 */
	private static final String bytesToHexStr(byte[] bcd)
	{
		StringBuffer s=new StringBuffer(bcd.length*2);

		for(int i=0;i<bcd.length;i++)
		{
			s.append(bcdLookup[(bcd[i]>>>4)&0x0f]);
			s.append(bcdLookup[bcd[i]&0x0f]);
		}

		return s.toString();
	}

	/**
	 * 将16进制字符串还原为字节数组.
	 */
	private static final byte[] hexStrToBytes(String s)
	{
		byte[] bytes;

		bytes=new byte[s.length()/2];

		for(int i=0;i<bytes.length;i++)
		{
			bytes[i]=(byte)Integer.parseInt(s.substring(2*i,2*i+2),16);
		}

		return bytes;
	}

	private static final char[] bcdLookup= {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};

	/**
	 * 生成费堆成RSA公钥和私钥
	 * KeyMap.get("public")为生成的公钥字符串,KeyMap.get("private")为生成的私钥字符串
	 * @return
	 */
	public static KeyPair buildRSAKey()
	{
		try
		{
			KeyPairGenerator keyGen=KeyPairGenerator.getInstance("RSA");
			SecureRandom random=new SecureRandom();
			String tempStr="1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
			int length=tempStr.length();
			String temp="";
			for(int i=0;i<50;i++)
			{
				int randomInt=random.nextInt(length);
				temp+=tempStr.substring(randomInt,randomInt+1);
			}
			random.setSeed(temp.getBytes());
			keyGen.initialize(1024,random);
			KeyPair key=keyGen.generateKeyPair();
			return key;
		}
		catch(NoSuchAlgorithmException e)
		{
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * @param srcString 需要加密的字符串
	 * @param key key值
	 * @param keyType 标识是用公钥加密还是用私钥加密 公钥:public 私钥:private
	 * @return
	 */
	public static String encryptByRSAKey(String srcString,String key,String keyType) 
	{
		try
		{
			byte[] byteEncodedKey=hexStrToBytes(key.trim());
			
			if(keyType!=null&&keyType.equals("public"))
			{
				//根据字符串生成PublicKey对象
				X509EncodedKeySpec keySpec=new X509EncodedKeySpec(byteEncodedKey);
				KeyFactory keyFactory=KeyFactory.getInstance("RSA");
				PublicKey pubKey=keyFactory.generatePublic(keySpec);
				//进行加密
				return SignatureUtil.encryptByRSAKey(srcString,pubKey);
			}
			else if(keyType!=null&&keyType.equals("private"))
			{
				//根据字符串生成PublicKey对象
				PKCS8EncodedKeySpec keySpec=new PKCS8EncodedKeySpec(byteEncodedKey);
				KeyFactory keyFactory=KeyFactory.getInstance("RSA");
				PrivateKey priKey=keyFactory.generatePrivate(keySpec);
				//进行加密
				return SignatureUtil.encryptByRSAKey(srcString,priKey);
			}
			else
			{
				return null;
			}
		}
		catch(InvalidKeyException e)
		{
			e.printStackTrace();
			return null;
		}
		catch(NoSuchAlgorithmException e)
		{
			e.printStackTrace();
			return null;
		}
		catch(InvalidKeySpecException e)
		{
			e.printStackTrace();
			return null;
		}
		catch(NoSuchPaddingException e)
		{
			e.printStackTrace();
			return null;
		}
		catch(IllegalBlockSizeException e)
		{
			e.printStackTrace();
			return null;
		}
		catch(BadPaddingException e)
		{
			e.printStackTrace();
			return null;
		}
		catch(IOException e)
		{
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 用私钥进行加密操作
	 * @param srcString 加密字符串
	 * @param privateKey 私钥
	 * @return
	 * @throws NoSuchAlgorithmException
	 * @throws NoSuchPaddingException
	 * @throws InvalidKeyException
	 * @throws IOException
	 * @throws IllegalBlockSizeException
	 * @throws BadPaddingException
	 */
	public static String encryptByRSAKey(String srcString,PrivateKey privateKey) throws NoSuchAlgorithmException,NoSuchPaddingException,InvalidKeyException,IOException,IllegalBlockSizeException,BadPaddingException
	{

		Cipher cipher=Cipher.getInstance("RSA/ECB/PKCS1Padding");
		cipher.init(Cipher.ENCRYPT_MODE,privateKey);
		byte[] data=srcString.getBytes("UTF-8");
		byte[] returnData=new byte[0];
		//该处这样处理是为了解决加密时超过117字节就报错的问题，进行分段加密
		for(int i=0;i<data.length;i+=100)
		{
			byte[] doFinal=cipher.doFinal(ArrayUtils.subarray(data,i,i+100));
			returnData=ArrayUtils.addAll(returnData,doFinal);
		}

//		BASE64Encoder encoder=new BASE64Encoder();
		return BASE64.encode(returnData);
	}
	/**
	 * 用公钥进行加密操作
	 * @param srcString 加密字符串
	 * @param publicKey 公钥
	 * @return
	 * @throws NoSuchAlgorithmException
	 * @throws NoSuchPaddingException
	 * @throws InvalidKeyException
	 * @throws IOException
	 * @throws IllegalBlockSizeException
	 * @throws BadPaddingException
	 */
	public static String encryptByRSAKey(String srcString,PublicKey publicKey) throws NoSuchAlgorithmException,NoSuchPaddingException,InvalidKeyException,IOException,IllegalBlockSizeException,BadPaddingException
	{

		Cipher cipher=Cipher.getInstance("RSA/ECB/PKCS1Padding");
		cipher.init(Cipher.ENCRYPT_MODE,publicKey);

		byte[] data=srcString.getBytes("UTF-8");
		byte[] returnData=new byte[0];
		//该处这样处理是为了解决加密时超过117字节就报错的问题，进行分段加密
		for(int i=0;i<data.length;i+=100)
		{
			byte[] doFinal=cipher.doFinal(ArrayUtils.subarray(data,i,i+100));
			returnData=ArrayUtils.addAll(returnData,doFinal);
		}

//		BASE64Encoder encoder=new BASE64Encoder();
		return BASE64.encode(returnData);
	}
	/**
	 * @param srcString 需要解密的字符串
	 * @param key key值
	 * @param keyType 标识是用公钥解密还是用私钥解密 公钥:public 私钥:private
	 * @return
	 */
	public static String decryptByRSAKey(String srcString,String key,String keyType) 
	{
		try
		{
			byte[] byteEncodedKey=hexStrToBytes(key.trim());
			
			if(keyType!=null&&keyType.equals("public"))
			{
				//根据字符串生成PublicKey对象
				X509EncodedKeySpec keySpec=new X509EncodedKeySpec(byteEncodedKey);
				KeyFactory keyFactory=KeyFactory.getInstance("RSA");
				PublicKey pubKey=keyFactory.generatePublic(keySpec);
				//进行解密
				return SignatureUtil.decryptByRSAKey(srcString,pubKey);
			}
			else if(keyType!=null&&keyType.equals("private"))
			{
				//根据字符串生成PrivateKey对象
				PKCS8EncodedKeySpec keySpec=new PKCS8EncodedKeySpec(byteEncodedKey);
				KeyFactory keyFactory=KeyFactory.getInstance("RSA");
				PrivateKey priKey=keyFactory.generatePrivate(keySpec);
				//进行解密
				return SignatureUtil.decryptByRSAKey(srcString,priKey);
			}
			else
			{
				return null;
			}
		}
		catch(InvalidKeyException e)
		{
			e.printStackTrace();
			return null;
		}
		catch(NoSuchAlgorithmException e)
		{
			e.printStackTrace();
			return null;
		}
		catch(InvalidKeySpecException e)
		{
			e.printStackTrace();
			return null;
		}
		catch(NoSuchPaddingException e)
		{
			e.printStackTrace();
			return null;
		}
		catch(IllegalBlockSizeException e)
		{
			e.printStackTrace();
			return null;
		}
		catch(BadPaddingException e)
		{
			e.printStackTrace();
			return null;
		}
		catch(IOException e)
		{
			e.printStackTrace();
			return null;
		}
		
	}

	/**
	 * 用公钥进行解密操作
	 * @param srcString 解密字符串
	 * @param publicKey 公钥
	 * @return
	 * @throws NoSuchAlgorithmException
	 * @throws NoSuchPaddingException
	 * @throws InvalidKeyException
	 * @throws IOException
	 * @throws IllegalBlockSizeException
	 * @throws BadPaddingException
	 */
	public static String decryptByRSAKey(String srcString,PublicKey publicKey) throws NoSuchAlgorithmException,NoSuchPaddingException,InvalidKeyException,IOException,IllegalBlockSizeException,BadPaddingException
	{
		Cipher cipher=Cipher.getInstance("RSA/ECB/PKCS1Padding");
		cipher.init(Cipher.DECRYPT_MODE,publicKey);

		StringBuilder sb=new StringBuilder();

//		BASE64Decoder decoder=new BASE64Decoder();
		byte[] data=BASE64.decode(srcString);
		//该处这样处理是为了解决解密时超过128字节就报错的问题，进行分段解密
		for(int i=0;i<data.length;i+=128)
		{
			byte[] doFinal=cipher.doFinal(ArrayUtils.subarray(data,i,i+128));
			sb.append(new String(doFinal));
		}
		return sb.toString();

	}
	/**
	 * 用私钥进行解密操作
	 * @param srcString 解密字符串
	 * @param privateKey 私钥
	 * @return
	 * @throws NoSuchAlgorithmException
	 * @throws NoSuchPaddingException
	 * @throws InvalidKeyException
	 * @throws IOException
	 * @throws IllegalBlockSizeException
	 * @throws BadPaddingException
	 */
	public static String decryptByRSAKey(String srcString,PrivateKey privateKey) throws NoSuchAlgorithmException,NoSuchPaddingException,InvalidKeyException,IOException,IllegalBlockSizeException,BadPaddingException
	{
		Cipher cipher=Cipher.getInstance("RSA/ECB/PKCS1Padding");
		cipher.init(Cipher.DECRYPT_MODE,privateKey);

		StringBuilder sb=new StringBuilder();
//		BASE64Decoder decoder=new BASE64Decoder();
		byte[] data=BASE64.decode(srcString);
		//该处这样处理是为了解决解密时超过128字节就报错的问题，进行分段解密
		for(int i=0;i<data.length;i+=128)
		{
			byte[] doFinal=cipher.doFinal(ArrayUtils.subarray(data,i,i+128));
			sb.append(new String(doFinal));
		}
		return sb.toString();

	}
	
	public static String encode(String src){
		return encryptByRSAKey(src, pubKey_Hex_Str, "public");
	}
	public static String decode(String code){
		return decryptByRSAKey(code, priKey_Hex_Str, "private");
	}

	public static void main(String args[]) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, IOException
	{
		KeyPair tKeyPair=SignatureUtil.buildRSAKey();
		
		String priKey = SignatureUtil.bytesToHexStr(tKeyPair.getPrivate().getEncoded());
		String pubKey = SignatureUtil.bytesToHexStr(tKeyPair.getPublic().getEncoded());
		String src = "123321";
		String code = SignatureUtil.encryptByRSAKey(src, priKey, "private");
		System.out.println(code);
		String decode = SignatureUtil.decryptByRSAKey(code, pubKey, "public");
		System.out.println(decode);
	}
}
